/*-
 *  Copyright (C) 2009 Peter Baldwin   
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.peterbaldwin.client.android.wildlife;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.provider.Settings;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.CursorAdapter;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.SimpleCursorAdapter;
import android.widget.Spinner;
import android.widget.TimePicker;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;

public class TurtleTally extends MapActivity implements View.OnClickListener,
		SimpleCursorAdapter.ViewBinder, DialogInterface.OnClickListener,
		AdapterView.OnItemSelectedListener, LocationListener {
	static final String LOG_TAG = "TurtleTally";

	private static final String PACKAGE_NAME_GMAIL = "com.google.android.gm";

	private static final String LOCATION_PROVIDER = LocationManager.GPS_PROVIDER;

	private static final int REQUEST_PHOTO = 1;

	private static Uri sUriPhoto;

	static {
		Uri.Builder builder = FileProvider.FILES_CONTENT_URI.buildUpon();

		String fileName = FileProvider.PARAM_FILE_NAME;
		builder.appendQueryParameter(fileName, "turtle.jpg");

		// TODO: What if output format is not JPEG?
		String contentType = FileProvider.PARAM_CONTENT_TYPE;
		builder.appendQueryParameter(contentType, "image/jpeg");

		sUriPhoto = builder.build();
	}

	/**
	 * Creates a {@link Cursor} using column values stored as {@code
	 * <string-array>}s.
	 * 
	 * @param resources
	 *            a reference to the {@link Resources} contain the column
	 *            values.
	 * @param columnNames
	 *            the names of the columns. Columns named
	 *            {@link BaseColumns#_ID} will result in column values equal to
	 *            the row index.
	 * @param columnIds
	 *            resource IDs for the {@code <string-array>}s containing the
	 *            column values. This value will be ignored if the corresponding
	 *            column name is {@link BaseColumns#_ID}.
	 * @return
	 */
	private static Cursor createCursor(Resources resources,
			String[] columnNames, int[] columnIds) {
		MatrixCursor cursor = new MatrixCursor(columnNames);
		Object[][] columns = new Object[columnIds.length][];
		int length = 0;

		// Get column values from resources
		for (int columnIndex = 0; columnIndex < columnIds.length; columnIndex++) {
			String columnName = columnNames[columnIndex];
			if (!columnName.equals(BaseColumns._ID)) {
				int id = columnIds[columnIndex];
				String[] column = resources.getStringArray(id);

				// All of the columns should be the same length, but using the
				// max value will result in an ArrayIndexOutOfBounds exception
				// when building the Cursor if they are not all the same length.
				length = Math.max(length, column.length);

				columns[columnIndex] = column;
			}
		}

		// Generate ID columns
		for (int columnIndex = 0; columnIndex < columnIds.length; columnIndex++) {
			String columnName = columnNames[columnIndex];
			if (columnName.equals(BaseColumns._ID)) {
				Integer[] column = new Integer[length];
				for (int row = 0; row < length; row++) {
					column[row] = row;
				}
				columns[columnIndex] = column;
			}
		}

		// Build cursor
		for (int rowIndex = 0; rowIndex < length; rowIndex++) {
			MatrixCursor.RowBuilder builder = cursor.newRow();
			for (int columnIndex = 0; columnIndex < columns.length; columnIndex++) {
				Object value = columns[columnIndex][rowIndex];
				builder.add(value);
			}
		}
		return cursor;
	}

	private static Cursor createSpeciesCursor(Resources resources) {
		String[] columnNames = { SpeciesColumns._ID, SpeciesColumns.NAME,
				SpeciesColumns.VALUE, SpeciesColumns.THUMBNAIL,
				SpeciesColumns.IMAGE };
		int[] columnIds = { -1, R.array.species_name, R.array.species_value,
				R.array.species_thumbnail, R.array.species_image };
		return createCursor(resources, columnNames, columnIds);
	}

	private static Cursor createHabitatCursor(Resources resources) {
		String[] columnNames = { HabitatColumns._ID, HabitatColumns.NAME,
				HabitatColumns.VALUE };
		int[] columnIds = { -1, R.array.habitat_name, R.array.habitat_value };
		return createCursor(resources, columnNames, columnIds);
	}

	private static Cursor createBehaviorCursor(Resources resources) {
		String[] columnNames = { BehaviorColumns._ID, BehaviorColumns.NAME,
				BehaviorColumns.VALUE };
		int[] columnIds = { -1, R.array.behavior_name, R.array.behavior_value };
		return createCursor(resources, columnNames, columnIds);
	}

	private static final String KEY_PHOTO = "photo";
	private static final String KEY_LATITUDE = "latitude";
	private static final String KEY_LONGITUDE = "longitude";
	private static final String KEY_ACCURACY = "accuracy";

	// TODO: button to remove photo

	private Bitmap mPhoto;

	private ImageView mImagePreview;
	private Button mButtonPhoto;
	private Button mButtonClearPhoto;
	private DatePicker mDatePicker;
	private TimePicker mTimePicker;
	private Spinner mSpinnerSpecies;
	private Button mButtonMagnify;
	private EditText mEditNotes;
	private MapView mMapView;
	private MapController mMapController;
	private View mHintLocation;
	private Button mButtonEnableGps;
	private Spinner mSpinnerHabitat;
	private Spinner mSpinnerBehavior;
	private Button mButtonSend;
	private Button mButtonCancel;

	private LocationManager mLocationManager;
	private LocationListener mLocationListener;

	private TurtleOverlay mOverlay;
	private double mLatitude;
	private double mLongitude;
	private float mAccuracy;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		Context context = this;
		Resources resources = getResources();

		mImagePreview = (ImageView) findViewById(R.id.image_preview);

		mButtonPhoto = (Button) findViewById(R.id.button_photo);
		mButtonPhoto.setOnClickListener(this);

		mButtonClearPhoto = (Button) findViewById(R.id.button_clear_photo);
		mButtonClearPhoto.setOnClickListener(this);

		mDatePicker = (DatePicker) findViewById(R.id.picker_date);

		mTimePicker = (TimePicker) findViewById(R.id.picker_time);

		{
			mSpinnerSpecies = (Spinner) findViewById(R.id.spinner_species);
			Cursor cursor = createSpeciesCursor(resources);
			String[] from = { SpeciesColumns.NAME, SpeciesColumns.THUMBNAIL };
			int[] to = { android.R.id.text1, android.R.id.icon };
			SimpleCursorAdapter adapter = new SimpleCursorAdapter(context,
					android.R.layout.simple_spinner_item, cursor, from, to);
			adapter.setDropDownViewResource(R.layout.species_spinner_item);
			adapter.setViewBinder(this);
			mSpinnerSpecies.setOnItemSelectedListener(this);
			mSpinnerSpecies.setAdapter(adapter);
		}

		mButtonMagnify = (Button) findViewById(R.id.button_magnify);
		mButtonMagnify.setOnClickListener(this);

		mEditNotes = (EditText) findViewById(R.id.edit_note);

		mMapView = (MapView) findViewById(R.id.map);
		mMapView.setClickable(false);
		mMapView.setBuiltInZoomControls(false);
		mMapView.setSatellite(true);
		mMapController = mMapView.getController();
		List<Overlay> overlays = mMapView.getOverlays();
		mOverlay = new TurtleOverlay();
		overlays.add(mOverlay);
		
		mHintLocation = findViewById(R.id.hint_location);
		
        mButtonEnableGps = (Button) findViewById(R.id.button_enable_gps);
        mButtonEnableGps.setOnClickListener(this);

		{
			mSpinnerHabitat = (Spinner) findViewById(R.id.spinner_habitat);
			Cursor cursor = createHabitatCursor(resources);
			String[] from = { HabitatColumns.NAME };
			int[] to = { android.R.id.text1 };
			SimpleCursorAdapter adapter = new SimpleCursorAdapter(context,
					android.R.layout.simple_spinner_item, cursor, from, to);
			int dropDownViewResource = android.R.layout.simple_spinner_dropdown_item;
			adapter.setDropDownViewResource(dropDownViewResource);
			mSpinnerHabitat.setOnItemSelectedListener(this);
			mSpinnerHabitat.setAdapter(adapter);
		}

		{
			mSpinnerBehavior = (Spinner) findViewById(R.id.spinner_behavior);
			Cursor cursor = createBehaviorCursor(resources);
			String[] from = { BehaviorColumns.NAME };
			int[] to = { android.R.id.text1 };
			SimpleCursorAdapter adapter = new SimpleCursorAdapter(context,
					android.R.layout.simple_spinner_item, cursor, from, to);
			int dropDownViewResource = android.R.layout.simple_spinner_dropdown_item;
			adapter.setDropDownViewResource(dropDownViewResource);
			mSpinnerBehavior.setOnItemSelectedListener(this);
			mSpinnerBehavior.setAdapter(adapter);
		}

		mButtonSend = (Button) findViewById(R.id.button_send);
		mButtonSend.setOnClickListener(this);

		mButtonCancel = (Button) findViewById(R.id.button_cancel);
		mButtonCancel.setOnClickListener(this);

		mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
		mLocationListener = this;

		if (savedInstanceState == null) {
			Calendar observationDate = Calendar.getInstance();
			int year = observationDate.get(Calendar.YEAR);
			int monthOfYear = observationDate.get(Calendar.MONTH);
			int dayOfMonth = observationDate.get(Calendar.DAY_OF_MONTH);
			int currentHour = observationDate.get(Calendar.HOUR_OF_DAY);
			int currentMinute = observationDate.get(Calendar.MINUTE);
			mDatePicker.updateDate(year, monthOfYear, dayOfMonth);
			mTimePicker.setCurrentMinute(currentMinute);
			mTimePicker.setCurrentHour(currentHour);
		}

		updateButtons();
	}

	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState) {
		super.onRestoreInstanceState(savedInstanceState);
		Bitmap photo = savedInstanceState.getParcelable(KEY_PHOTO);
		setPhoto(photo);
		mLatitude = savedInstanceState.getDouble(KEY_LATITUDE);
		mLongitude = savedInstanceState.getDouble(KEY_LONGITUDE);
		mAccuracy = savedInstanceState.getFloat(KEY_ACCURACY);
		
		// The hour is not shown unless we do this:
		mTimePicker.setCurrentMinute(mTimePicker.getCurrentMinute());
		mTimePicker.setCurrentHour(mTimePicker.getCurrentHour());
		
		updateButtons();
	}

	@Override
	protected void onResume() {
		super.onResume();
		
		// Assume provider is enabled until onProviderDisabled callback
		onProviderEnabled(LOCATION_PROVIDER);
		
		// Use the same values as the Camera app:
		// http://google.com/codesearch/p?hl=en#envDSvXc3DY/src/com/android/camera/Camera.java
		mLocationManager.requestLocationUpdates(LOCATION_PROVIDER, 1000, 0F,
				mLocationListener);
	}

	@Override
	protected void onPause() {
		mLocationManager.removeUpdates(mLocationListener);
		super.onPause();
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
	}

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}

	private static boolean isSet(Spinner spinner, String columnName) {
		CharSequence value = getSpinnerValue(spinner, columnName);
		return !"none".equals(value);
	}

	private void updateButtons() {
		CharSequence image = getSpinnerValue(mSpinnerSpecies,
				SpeciesColumns.IMAGE);
		mButtonMagnify.setEnabled(image.length() != 0);

		boolean ready = isSet(mSpinnerSpecies, SpeciesColumns.VALUE)
				&& isSet(mSpinnerHabitat, HabitatColumns.VALUE)
				&& isSet(mSpinnerBehavior, BehaviorColumns.VALUE);
		mButtonSend.setEnabled(ready);
	}

	private static CharSequence getSpinnerValue(Spinner spinner,
			String columnName) {
		CursorAdapter adapter = (CursorAdapter) spinner.getAdapter();
		Cursor cursor = adapter.getCursor();
		int position = spinner.getSelectedItemPosition();
		cursor.moveToPosition(position);
		int columnIndex = cursor.getColumnIndex(columnName);
		return cursor.getString(columnIndex);
	}

	/**
	 * Starts an email {@link Activity} for the given {@link Intent}.
	 * 
	 * TODO: Add support for Mail application when it accepts extras for body
	 * and subject.
	 * 
	 * @return {@code true} if an appropriate email {@link Activity} was found,
	 *         {@code false} otherwise.
	 */
	private boolean startEmailActivity(Intent intent) {
		PackageManager pm = getPackageManager();
		int flags = PackageManager.MATCH_DEFAULT_ONLY;
		List<ResolveInfo> matches = pm.queryIntentActivities(intent, flags);
		for (ResolveInfo info : matches) {
			if (info.activityInfo.enabled) {
				String packageName = info.activityInfo.packageName;
				String className = info.activityInfo.name;
				if (packageName.equals(PACKAGE_NAME_GMAIL)) {
					intent.setClassName(packageName, className);
					startActivity(intent);
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * This method makes it non-trivial for spammers to scrape the email
	 * addresses from this source code.
	 */
	private static String email(String username, String domain) {
		return username + '@' + domain;
	}

	private void send() {
		String[] emails = { email("aap", "torontozoo.ca") };
		String[] cc = {};
		String[] bcc = {};
		String subject = "Ontario Turtle Tally";

		int year = mDatePicker.getYear();
		int month = mDatePicker.getMonth();
		int day = mDatePicker.getDayOfMonth();
		int hour = mTimePicker.getCurrentHour();
		int minute = mTimePicker.getCurrentMinute();

		Calendar calendar = Calendar.getInstance();
		calendar.set(Calendar.YEAR, year);
		calendar.set(Calendar.MONTH, month);
		calendar.set(Calendar.DAY_OF_MONTH, day);
		calendar.set(Calendar.HOUR_OF_DAY, hour);
		calendar.set(Calendar.MINUTE, minute);

		Date date = calendar.getTime();

		StringBuilder builder = new StringBuilder();

		DateFormat dateFormat = new SimpleDateFormat("dd/MM/yy");
		String formattedDate = dateFormat.format(date);
		builder.append("Date: ").append(formattedDate).append('\n');

		DateFormat timeFormat = new SimpleDateFormat("h:mm a");
		String formattedTime = timeFormat.format(date);
		builder.append("Time: ").append(formattedTime).append('\n');

		CharSequence species = getSpinnerValue(mSpinnerSpecies,
				SpeciesColumns.NAME);
		builder.append("Species: ").append(species).append('\n');

		String notes = mEditNotes.getText().toString();
		builder.append("Observations:\n");
		builder.append(notes);
		builder.append("\n\n");

		CharSequence latitude = String.valueOf(mLatitude);
		builder.append("Latitude: ").append(latitude).append('\n');
		CharSequence longitude = String.valueOf(mLongitude);
		builder.append("Longitude: ").append(longitude).append('\n');

		CharSequence habitat = getSpinnerValue(mSpinnerHabitat,
				HabitatColumns.NAME);
		builder.append("Habitat: ").append(habitat).append('\n');

		CharSequence behavior = getSpinnerValue(mSpinnerBehavior,
				BehaviorColumns.NAME);
		builder.append("Behavior: ").append(behavior).append('\n');

		String text = builder.toString();

		Intent intent = new Intent(Intent.ACTION_SEND);
		if (mPhoto != null) {
			String contentType = FileProvider.getContentType(sUriPhoto);
			intent.setType(contentType);
			intent.putExtra(Intent.EXTRA_STREAM, sUriPhoto);
		} else {
			intent.setType("text/plain");
		}
		intent.putExtra(Intent.EXTRA_EMAIL, emails);
		intent.putExtra(Intent.EXTRA_CC, cc);
		intent.putExtra(Intent.EXTRA_BCC, bcc);
		intent.putExtra(Intent.EXTRA_SUBJECT, subject);
		intent.putExtra(Intent.EXTRA_TEXT, text);

		// TODO: Append personal data (or is email reply-to sufficient?)
		// TODO: Attach image
		// TODO: Append strapline

		if (startEmailActivity(intent)) {
			finish();
		} else {
			error(R.string.error_gmail_missing);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
		int id = view.getId();
		if (id == android.R.id.icon) {
			Resources resources = getResources();
			String name = cursor.getString(columnIndex);
			if (name.length() != 0) {
				String defType = "drawable";
				String defPackage = getPackageName();
				int resId = resources.getIdentifier(name, defType, defPackage);

				ImageView imageView = (ImageView) view;
				imageView.setImageResource(resId);
			}
			return true;
		}
		return false;
	}

	private void cancel() {
		Context context = this;
		AlertDialog.Builder builder = new AlertDialog.Builder(context);
		builder.setMessage(R.string.confirm_cancel);
		builder.setPositiveButton(R.string.yes, this);
		builder.setNegativeButton(R.string.no, null);
		AlertDialog dialog = builder.create();
		dialog.show();
	}

	private void error(int message) {
		Context context = this;
		AlertDialog.Builder builder = new AlertDialog.Builder(context);
		builder.setTitle(R.string.title_error);
		builder.setMessage(message);
		builder.setPositiveButton(R.string.ok, null);
		AlertDialog dialog = builder.create();
		dialog.show();
	}

	/**
	 * Show a larger image of the selected species.
	 */
	private void magnify() {
		CharSequence image = getSpinnerValue(mSpinnerSpecies,
				SpeciesColumns.IMAGE);
		String name = String.valueOf(image);
		if (name.length() != 0) {
			Uri.Builder builder = AssetProvider.CONTENT_URI.buildUpon();
			builder.appendQueryParameter(AssetProvider.PARAM_FILE_NAME, name);
			builder.appendQueryParameter(AssetProvider.PARAM_CONTENT_TYPE,
					"image/jpeg");

			Uri uri = builder.build();
			Intent intent = new Intent(Intent.ACTION_VIEW, uri);
			startActivity(intent);
		}
	}

	private void takePhoto() {
		Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
		intent.putExtra(MediaStore.EXTRA_OUTPUT, sUriPhoto);
		startActivityForResult(intent, REQUEST_PHOTO);
	}

	public final OutputStream openOutputStream(Uri uri, String mode)
			throws FileNotFoundException {
		ContentResolver resolver = getContentResolver();
		ParcelFileDescriptor fd = resolver.openFileDescriptor(uri, mode);
		if (fd != null) {
			return new ParcelFileDescriptor.AutoCloseOutputStream(fd);
		} else {
			return null;
		}
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (requestCode == REQUEST_PHOTO) {
			if (resultCode == RESULT_OK) {
				new ThumbnailTask().execute(sUriPhoto);
			}
		} else {
			super.onActivityResult(requestCode, resultCode, data);
		}
	}

	private void setPhoto(Bitmap bitmap) {
		mPhoto = bitmap;
		if (bitmap != null) {
			mImagePreview.setImageBitmap(bitmap);
			mImagePreview.setVisibility(View.VISIBLE);
			mButtonPhoto.setText(R.string.button_retake);
			mButtonClearPhoto.setVisibility(View.VISIBLE);
		} else {
			mImagePreview.setVisibility(View.GONE);
			mButtonPhoto.setText(R.string.button_photo);
			mButtonClearPhoto.setVisibility(View.GONE);
		}
	}

	/**
     * {@inheritDoc}
     */
    public void onClick(View v) {
		if (v == mButtonSend) {
			send();
		} else if (v == mButtonCancel) {
			cancel();
		} else if (v == mButtonMagnify) {
			magnify();
		} else if (v == mButtonPhoto) {
			takePhoto();
		} else if (v == mButtonClearPhoto) {
			setPhoto(null);
		} else if (v == mButtonEnableGps) {
		    Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
		    startActivity(intent);
		}
	}

    /**
     * {@inheritDoc}
     */
    public void onClick(DialogInterface dialog, int which) {
		finish();
	}

    /**
     * {@inheritDoc}
     */
    public void onItemSelected(AdapterView<?> parent, View view, int position,
			long id) {
		updateButtons();
	}

    /**
     * {@inheritDoc}
     */
    public void onNothingSelected(AdapterView<?> parent) {
		updateButtons();
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		if (mPhoto != null) {
			outState.putParcelable(KEY_PHOTO, mPhoto);
		}
		outState.putDouble(KEY_LATITUDE, mLatitude);
		outState.putDouble(KEY_LONGITUDE, mLongitude);
		outState.putFloat(KEY_ACCURACY, mAccuracy);
	}

	/**
     * {@inheritDoc}
     */
    public void onLocationChanged(Location location) {
		mLatitude = location.getLatitude();
		mLongitude = location.getLongitude();
		mAccuracy = location.getAccuracy();

		int latitudeE6 = (int) (mLatitude * 1E6);
		int longitudeE6 = (int) (mLongitude * 1E6);
		GeoPoint point = new GeoPoint(latitudeE6, longitudeE6);
		mOverlay.setTurtlePosition(point);
		mMapController.setCenter(point);
		mMapController.setZoom(19);
	}

    /**
     * {@inheritDoc}
     */
    public void onProviderDisabled(String provider) {
        mMapView.setVisibility(View.GONE);
        mHintLocation.setVisibility(View.GONE);
        mButtonEnableGps.setVisibility(View.VISIBLE);
	}

    /**
     * {@inheritDoc}
     */
    public void onProviderEnabled(String provider) {
        mMapView.setVisibility(View.VISIBLE);
        mHintLocation.setVisibility(View.VISIBLE);
        mButtonEnableGps.setVisibility(View.GONE);
	}

    /**
     * {@inheritDoc}
     */
    public void onStatusChanged(String provider, int status, Bundle extras) {
	}

	public class ThumbnailTask extends AsyncTask<Uri, Integer, Bitmap> {

		@Override
		protected Bitmap doInBackground(Uri... uri) {
			try {
				ContentResolver resolver = getContentResolver();
				InputStream is = resolver.openInputStream(sUriPhoto);
				try {
					BitmapFactory.Options opts = new BitmapFactory.Options();
					opts.inSampleSize = 2;
					return BitmapFactory.decodeStream(is, null, opts);
				} finally {
					is.close();
				}
			} catch (IOException e) {
				return null;
			}
		}

		@Override
		protected void onPostExecute(Bitmap result) {
			setPhoto(result);
		}
	}
	
	private class TurtleOverlay extends ItemizedOverlay<OverlayItem> {

		private OverlayItem mTurtle;

		public TurtleOverlay() {
			super(boundCenter(getResources().getDrawable(R.drawable.icon)));
		}
		
		public void setTurtlePosition(GeoPoint point) {
			if (point != null) {
				mTurtle = new OverlayItem(point, "Turtle", point.toString());
			} else {
				mTurtle = null;
			}
			populate();
		}

		@Override
		protected OverlayItem createItem(int index) {
			if (mTurtle != null && index == 0) {
				return mTurtle;
			} else {
				throw new IndexOutOfBoundsException();
			}
		}

		@Override
		public int size() {
			return mTurtle != null ? 1 : 0;
		}
	}
}